Skip to content

Make native parser the default - #21823

Open
ilevkivskyi wants to merge 22 commits into
python:masterfrom
ilevkivskyi:default-native-parser
Open

Make native parser the default#21823
ilevkivskyi wants to merge 22 commits into
python:masterfrom
ilevkivskyi:default-native-parser

Conversation

@ilevkivskyi

Copy link
Copy Markdown
Member

For now the PR only contains updates for different error messages in the new parser. As I started working on making native parser the default, I discovered there are still several small bugs/inconsistencies.

I will start fixing them one by one in separate PRs, but you are also welcome to fix some if you have time, just let me know before you start working on them.

There is one question to discuss for now: it looks like there is no support in new parser for type comments in for and with statements. I vaguely remember this is intentional. What is our stance on this? Do we declare type comments for for and with deprecated and thus only supported by the old parser?

cc @JukkaL @hauntsaninja

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@hauntsaninja hauntsaninja left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we declare type comments for for and with deprecated and thus only supported by the old parser?

This seems totally fine to me. It doesn't affect public interfaces so there isn't going to be much fallout from losing these types.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

@JukkaL I completely forgot that some tests were already skipped with _no_native_parse. So in fact we have a bit more things to fix. Fortunately, it looks like all of them are easy. But this still means a bit more work is needed.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

(I will start with all the fixes that require changes in ast_serialize, and will handle the mypy-only things later)

@github-actions

This comment has been minimized.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

@JukkaL I have been looking into test failures, and one test case looks weird. I think the current behavior in testOverloadIfSplitFunctionDef (with the old parser) is wrong, while the behavior with the new parser is actually correct (note there is # flags: --always-true True so that behavior for f1 and f3 should be identical). Just wanted to double-check I am not missing something.

JukkaL pushed a commit that referenced this pull request Aug 25, 2026
This matches behavior of the new parser to the old one. Not adding
tests, since some existing tests fail when switched to new parser, see
#21823
JukkaL pushed a commit that referenced this pull request Aug 25, 2026
Old parser has some special treatment for docstrings. Match that in the
new parser. Not adding tests, since some existing tests fail when
switched to new parser, see #21823
@ilevkivskyi

Copy link
Copy Markdown
Member Author

@JukkaL Few more things for our discussion today:

  • Ruff parser has a problem with parsing string that contain unicode surrogates, they all get replaced with the same replacement/placeholder symbol, see Surrogate code points are internally represented as U+FFFD REPLACEMENT CHARACTER astral-sh/ruff#13666. This is not a problem for mypy, but it is a problem for mypyc, as it affects runtime behavior, thus testUnicodeSurrogate fails. For some reasons fixing this is non-trivial, so RustPython (Rust-based Python interpreter that also uses Ruff parser) has an ~1000 lines patch that uses WTF, see Parse surrogates in string literals properly RustPython/RustPython#5629
  • There are some cases where new parser errors are worse (either more verbose or more generic) than old parser errors. I am not going to spend much time on this, unless you think this is important.
  • New parser does not error on some invalid function/variable type comments, and simply ignores them. I am not sure what to do about that, but I guess it may be fine.
  • New parser ignores invalid # type: ignore comments instead of giving an error. I think this is an important thing to decide, potentially we may want to replicate old behavior exactly.
  • New parser forcefully adds escapes even when they are not needed, for example b"what's up" is parsed as b"what\'s up". TBH I don't really like this, what is the motivation for this?

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@ilevkivskyi ilevkivskyi changed the title [WIP] Make native parser the default Make native parser the default Sep 7, 2026
@github-actions

This comment has been minimized.

1 similar comment
@github-actions

This comment has been minimized.

@ilevkivskyi

Copy link
Copy Markdown
Member Author

Oh, it looks like there are 4 more issues (previously unnoticed because they appear only in some CI jobs):

  • Version check for *-unpacking is completely broken, we allow foo[*x] on Python 3.10, but prohibit Callable[[*Ts], int] which is fine
  • There is something wrong with newlines in f-strings on Windows
  • There are couple more edge cases where new parser ignores type comments silently instead of giving an error
  • This one looks like a bug in Ruff parser: it allows un-parenthesized tuples as exception types (which is a SyntaxError in Python)

All except the Windows thing look like easy fixes, I will try to do this today and make another ast-serialize release.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@danielhollas

Copy link
Copy Markdown

This one looks like a bug in Ruff parser: it allows un-parenthesized tuples as exception types (which is a SyntaxError in Python)

This is allowed since python 3.14 per PEP-758

@ilevkivskyi

Copy link
Copy Markdown
Member Author

@danielhollas Oh, thanks! I will make the check conditional on Python version.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

prefect (https://github.com/PrefectHQ/prefect)
- src/prefect/utilities/collections.py:504: error: "None" object is not iterable  [misc]
+ src/prefect/utilities/collections.py:505: error: "None" object is not iterable  [misc]
- src/prefect/_internal/concurrency/api.py:181: error: "Call" expects no type arguments, but 1 given  [type-arg]
- src/prefect/_internal/concurrency/api.py:181: error: Variable "prefect._internal.concurrency.api.T" is not valid as a type  [valid-type]
+ src/prefect/_internal/concurrency/api.py:182: error: Variable "prefect._internal.concurrency.api.T" is not valid as a type  [valid-type]
- src/prefect/_internal/concurrency/api.py:181: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
+ src/prefect/_internal/concurrency/api.py:182: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
+ src/prefect/_internal/concurrency/api.py:183: error: "Call" expects no type arguments, but 1 given  [type-arg]
+ src/prefect/_internal/concurrency/api.py:183: error: Variable "prefect._internal.concurrency.api.T" is not valid as a type  [valid-type]
+ src/prefect/_internal/concurrency/api.py:183: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases

colour (https://github.com/colour-science/colour)
- colour/characterisation/aces_it.py:975: error: Ellipses cannot accompany other parameter types in function type signature  [syntax]
+ colour/characterisation/aces_it.py:976: error: Unexpected "..."  [misc]
+ colour/characterisation/aces_it.py:977: error: Unexpected "..."  [misc]

antidote (https://github.com/Finistere/antidote)
- src/antidote/lib/interface_ext/__init__.py:1359: error: "staticmethod" expects 2 type arguments, but 1 given  [type-arg]
+ src/antidote/lib/interface_ext/__init__.py:1361: error: "staticmethod" expects 2 type arguments, but 1 given  [type-arg]
- src/antidote/lib/interface_ext/__init__.py:1359: error: Missing type arguments for generic type "staticmethod"  [type-arg]
+ src/antidote/lib/interface_ext/__init__.py:1361: error: Missing type arguments for generic type "staticmethod"  [type-arg]

Tanjun (https://github.com/FasterSpeeding/Tanjun)
- tanjun/commands/menu.py:420: error: Type argument "_UserCallbackSigT" of "MessageCommand" must be a subtype of "def (MessageContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
+ tanjun/commands/menu.py:422: error: Type argument "_UserCallbackSigT" of "MessageCommand" must be a subtype of "def (MessageContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
- tanjun/commands/menu.py:420: error: Type argument "_UserCallbackSigT" of "SlashCommand" must be a subtype of "def (SlashContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
+ tanjun/commands/menu.py:422: error: Type argument "_UserCallbackSigT" of "SlashCommand" must be a subtype of "def (SlashContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
- tanjun/commands/menu.py:420: error: Type argument "_MessageCallbackSigT" of "MessageCommand" must be a subtype of "def (MessageContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
+ tanjun/commands/menu.py:424: error: Type argument "_MessageCallbackSigT" of "MessageCommand" must be a subtype of "def (MessageContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
- tanjun/commands/menu.py:420: error: Type argument "_MessageCallbackSigT" of "SlashCommand" must be a subtype of "def (SlashContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]
+ tanjun/commands/menu.py:424: error: Type argument "_MessageCallbackSigT" of "SlashCommand" must be a subtype of "def (SlashContext, /, *Any, **Any) -> Coroutine[Any, Any, None]"  [type-var]

pandera (https://github.com/pandera-dev/pandera)
+ tests/pandas/test_model.py:170: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:172: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:172: note: Cannot use a function call in a type annotation
+ tests/pandas/test_model.py:282: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:283: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:283: note: Cannot use a function call in a type annotation
+ tests/pandas/test_model.py:285: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:286: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:286: note: Cannot use a function call in a type annotation
+ tests/pandas/test_model.py:290: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:291: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:291: note: Cannot use a function call in a type annotation
+ tests/pandas/test_model.py:293: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:294: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:294: note: Cannot use a function call in a type annotation
+ tests/pandas/test_model.py:296: error: Unused "type: ignore" comment  [unused-ignore]
+ tests/pandas/test_model.py:297: error: Invalid type comment or annotation  [valid-type]
+ tests/pandas/test_model.py:297: note: Cannot use a function call in a type annotation

ibis (https://github.com/ibis-project/ibis)
- ibis/selectors.py:434: error: Variable "ibis.common.collections.frozendict" is not valid as a type  [valid-type]
+ ibis/selectors.py:437: error: Variable "ibis.common.collections.frozendict" is not valid as a type  [valid-type]
- ibis/selectors.py:434: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
+ ibis/selectors.py:437: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
- ibis/expr/types/generic.py:152: error: Function "ibis.expr.types.generic.Value.type" is not valid as a type  [valid-type]
+ ibis/expr/types/generic.py:164: error: Function "ibis.expr.types.generic.Value.type" is not valid as a type  [valid-type]
- ibis/expr/types/generic.py:152: note: Perhaps you need "Callable[...]" or a callback protocol?
+ ibis/expr/types/generic.py:164: note: Perhaps you need "Callable[...]" or a callback protocol?
- ibis/expr/types/generic.py:170: error: Function "ibis.expr.types.generic.Value.type" is not valid as a type  [valid-type]
+ ibis/expr/types/generic.py:171: error: Function "ibis.expr.types.generic.Value.type" is not valid as a type  [valid-type]
- ibis/expr/types/generic.py:170: note: Perhaps you need "Callable[...]" or a callback protocol?
+ ibis/expr/types/generic.py:171: note: Perhaps you need "Callable[...]" or a callback protocol?
- ibis/expr/types/temporal.py:428: error: "Value" expects no type arguments, but 1 given  [type-arg]
+ ibis/expr/types/temporal.py:431: error: "Value" expects no type arguments, but 1 given  [type-arg]
- ibis/expr/types/temporal.py:1161: error: "Value" expects no type arguments, but 1 given  [type-arg]
+ ibis/expr/types/temporal.py:1174: error: "Value" expects no type arguments, but 1 given  [type-arg]

static-frame (https://github.com/static-frame/static-frame)
+ static_frame/core/frame.py:7192: error: Unused "type: ignore" comment  [unused-ignore]
+ static_frame/core/frame.py:7193: error: Need type annotation for "name"  [var-annotated]
+ static_frame/core/frame.py:7435: error: Unused "type: ignore" comment  [unused-ignore]
+ static_frame/core/frame.py:7436: error: Need type annotation for "name"  [var-annotated]

apprise (https://github.com/caronc/apprise)
- apprise/plugins/fluxer.py:512: error: Unsupported operand types for + ("str" and "bool")  [operator]
+ apprise/plugins/fluxer.py:513: error: Unsupported operand types for + ("str" and "bool")  [operator]
- apprise/plugins/fluxer.py:512: note: Right operand is of type "bool | str"
+ apprise/plugins/fluxer.py:513: note: Right operand is of type "bool | str"
- apprise/plugins/fluxer.py:512: error: Incompatible types in assignment (expression has type "Any | str", target has type "bool")  [assignment]
+ apprise/plugins/fluxer.py:513: error: Incompatible types in assignment (expression has type "Any | str", target has type "bool")  [assignment]

@ilevkivskyi

Copy link
Copy Markdown
Member Author

OK, there is just one issue left, the CRLF failure on Windows. The problem is that when reading a source file in Python, it has this concept of universal newlines, so that e.g. \r\n is automatically translated into \n, while if we do this in Rust using std::fs::read_to_string() we get the original newlines. In most cases this doesn't matter, except when we compile multi-line strings in mypyc. In this case Ruff parser preserves the newlines in the string literals as well, so we (incorrectly) get

repr("""foo
bar""") == "'foo\r\nbar'"

on Windows. It seems to me we cannot fix the newlines for string literals only, because we will need to preserve the escaped ones, handle f-strings and t-strings, and it all gets quite tricky. The only feasible solution I see is to replace all newlines in the source code before sending it to the Ruff parser. @JukkaL what do you think?

@ilevkivskyi

Copy link
Copy Markdown
Member Author

@JukkaL Btw I have found a crate that does some (very) fast newline normalization https://crates.io/crates/newline_normalizer (it however has very few downloads).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants